﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using CrmCustomInstallActions.CrmSdk;
using CrmCustomInstallActions.MetadataServiceSdk;

namespace CrmCustomInstallActions
{
    /// <summary>
    /// Determines the command mode: Install/Uninstall
    /// Commands which returns FALSE in the SupportsUninstall property of the Command Interface
    /// will not be Initialized and executed with the CommandModes.Uninstall parameter
    /// </summary>
    public enum CommandModes : int
    {
        Install = 1,
        Uninstall = 2,
        Rollback = 3
    }

    /// <summary>
    /// This is the interface all Crm Solution installer commands must implement.
    /// It is recommended that all commands inherit the abstract class GenericCrmSolutionCommand, which
    /// also implementes the ICrmSolutionCommand interface
    /// </summary>
    interface ICrmSolutionCommand
    {
        /// <summary>
        /// The Crm solution installer will call this method first to initialize the command
        /// The command is only executed, if this method returns TRUE
        /// </summary>
        /// <param name="CommandName">The name of the command</param>
        /// <param name="LogFile">The path and filename of the installation/uninstallation Log file. The command is responsible for closing the file after it has written the last log entry to the file</param>
        /// <param name="Configuration">Contains the Configuration element of the settings.xml file. This is specific for each command</param>
        /// <param name="PropertyBag">A Dictionary which contains cross-command properties, like a reference to the CrmService, MetadataService, etc. The command can also add to this Dictionary</param>
        /// <param name="ReportProgressCallback">A delegate for the command to report its progress on</param>
        /// <returns>TRUE if the command was initialized correctly. Otherwise FALSE</returns>
        bool InitializeCommand(string CommandName, string LogFile, XmlDocument Configuration, ref Dictionary<string, object> PropertyBag, ReportProgressDelegate ReportProgressCallback);

        /// <summary>
        /// Executes the command. This method will only be called if the InitializeCommand method returns TRUE
        /// </summary>
        /// <param name="CommandMode">Specicifies, if the command should execute in Install or Uninstall mode</param>
        /// <returns>True if the command executed correctly, otherwise FALSE. If the command returns FALSE in Install mode, the crm solution installer will begin to rollback the previous executed commands in Uninstall mode</returns>
        bool ExecuteCommand(CommandModes CommandMode);

        /// <summary>
        /// This method is executed after the ExecuteCommand. The Command should release all its resources in this method
        /// particularly the Log file should be closed, so it is ready for the next command
        /// </summary>
        /// <returns></returns>
        bool ReleaseResources();



        /// <summary>
        /// A Dictionary for messages, that the command can use to show to the end-user if the command does not execute properly (returns FALSE in the execute method)
        /// These messages will typically be Exceptions collected during execution, but can also be regular information for the end-user.
        /// </summary>
        /// <returns></returns>
        Dictionary<string, string> GetMessages();

        /// <summary>
        /// Determines if the specific command supports the Uninstall mode
        /// </summary>
        bool SupportsUninstall { get; }

        /// <summary>
        /// Determines if the specific command supports user interface actions like Messageboxes, etc.
        /// </summary>
        bool SupportsUserInterface { get; }

    }

    /// <summary>
    /// A delegate that points to a function in the Crm Solution installer, for reporting progress on the specific command. This should be used in commands which executes multiple actions on the CRM system
    /// </summary>
    /// <param name="PercentComplete">an integer from 0 to 100, that controls the progress bar</param>
    /// <param name="Message">The message to display to the end-user using the installer</param>
    internal delegate void ReportProgressDelegate(int PercentComplete, string Message);


    /// <summary>
    /// This is the base class for all CrmSolutionCommands. Specific Commands should inherit from this class
    /// </summary>
    internal abstract class GenericCrmSolutionCommand : ICrmSolutionCommand
    {

        #region private/protected fields

        private StreamWriter file = null;
        protected string backupFolder = "";
        protected Dictionary<string, string> Exceptions = new Dictionary<string, string>();
        protected XmlDocument Config = null;
        protected Dictionary<string, object> Props = null;
        protected CrmService service = null;
        protected MetadataService metadata = null;
        protected ReportProgressDelegate ReportProgress = null;
        protected string name = "";
        protected string TargetDir = "";
        private string logFile;

        #endregion

        protected void WriteLog(string Message)
        {
            try
            {
                file = System.IO.File.AppendText(logFile);
                if (file != null)
                {
                    file.WriteLine("{0}:\t{1}", DateTime.Now.ToString(), Message);
                    file.Close();
                }

            }
            catch (Exception e)
            {
                Exceptions.Add("LogFileError" + DateTime.Now.ToString("s"), e.Message + "\r\nMessage not written: " + Message);
            }
        }

        #region ICrmSolutionCommand Members

        public virtual bool ReleaseResources()
        {
            try
            {
                if (file != null)
                {
                    file.Flush();
                    file.Close();
                }

                service = null;
                metadata = null;
                Props = null;
                Config = null;
                Exceptions = null;
                ReportProgress = null;
                return true;

            }
            catch (Exception)
            {
                return false;
            }
        }


        public virtual bool InitializeCommand(string CommandName, string LogFile, XmlDocument Configuration, ref Dictionary<string, object> PropertyBag, ReportProgressDelegate ProgressCallback)
        {

            // Set the input paramteres

            name = CommandName;

            try
            {
                logFile = LogFile;
                file = File.CreateText(LogFile);
            }
            catch (Exception e)
            {
                Exceptions.Add("Unable to create or open the log file '" + LogFile + ". Check that the path and file is valid and exists", e.Message);
                return false;
            }

            try
            {
                Config = Configuration;
                Props = PropertyBag;
            }
            catch (Exception e)
            {
                Exceptions.Add("Unable to initialize the configuration and properties for this command. Check that the solution Configuration file is valid", e.Message);
                return false;
            }

            if (ProgressCallback != null)
            {
                ReportProgress = ProgressCallback;
            }

            // Get the Target Application folder
            if (!Props.ContainsKey("TARGETDIR"))
            {
                Exceptions.Add("The Application folder is missing from the Crm Solution Installer", "This is a system error");
                return false;
            }
            else
            {
                TargetDir = Convert.ToString(Props["TARGETDIR"]);
            }

            // Get the backup folder
            if (!Props.ContainsKey("BACKUPFOLDER"))
            {
                Exceptions.Add("The backup folder is missing from the Crm Solution Installer", "This is a system error");
                return false;
            }
            else
            {
                backupFolder = Convert.ToString(Props["BACKUPFOLDER"]);
            }

            // Get the CRM Service reference and the Metadata reference from the propertyBag
            if (!Props.ContainsKey("CRMSDK"))
            {
                Exceptions.Add("The property reference to the CRM Service is missing. Check that the reference is set up correct", "This is a system error");
                return false;
            }
            else
            {
                service = (CrmService)Props["CRMSDK"];
            }

            if (!Props.ContainsKey("METADATASERVICESDK"))
            {
                Exceptions.Add("The property reference to the CRM Metadata Service is missing. Check that the reference is set up correct", "This is a system error");
                return false;
            }
            else
            {
                metadata = (MetadataService)Props["METADATASERVICESDK"];
            }


            if (InitializeSpecificCommand())
            {
                WriteLog("Succesfully initialized the command '" + name + "'");
                return true;
            }
            else
            {
                WriteLog("Failed to initialize the command '" + name + "'");
                file.Flush();
                file.Close();
                return false;
            }
        }

        internal abstract bool InitializeSpecificCommand();


        public abstract bool ExecuteCommand(CommandModes CommandMode);


        public Dictionary<string, string> GetMessages()
        {
            return Exceptions;
        }

        public abstract bool SupportsUninstall { get; }

        public abstract bool SupportsUserInterface { get; }

        #endregion

        #region ICrmSolutionCommand Members


       
        #endregion
    }








}
